package com.mbc.hr.manager;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.persistence.NoResultException;

import com.mbc.common.CommonConstants.RecordStatus;
import com.mbc.common.entities.User;
import com.mbc.common.entities.UserInBranches;
import com.mbc.common.entities.UserInRoles;
import com.mbc.common.exception.DataDuplicationException;
import com.mbc.common.exception.UserAlreadyExistException;
import com.mbc.common.manager.AbstractMgr;
import com.mbc.common.param.CriteriaParams;
import com.mbc.common.utils.KeyGenerator;
import com.mbc.hr.data.EmployeeDataList;
import com.mbc.hr.entities.Employee;
import com.mbc.hr.entities.EmployeeEducation;
import com.mbc.hr.entities.EmployeeExperience;
import com.mbc.hr.entities.EmployeeGroup;
import com.mbc.hr.entities.EmployeePersonal;
import com.mbc.hr.entities.LeaveBalance;
import com.mbc.hr.entities.LeaveSchemeDetail;

public class EmployeeMgr extends AbstractMgr {
	
	public EmployeeMgr(){}
	
	public EmployeeMgr(String sessionId){
		this.sessionId = sessionId;
	}
	
	/**
	 * Updating finger print ID from device
	 * @param employeeNo
	 * @param fingerPrintID
	 * @throws Exception
	 */
	public void updateFingerPrintID(String employeeNo, long fingerPrintID)throws Exception{
		openConnection();
		try {
			beginTransaction();
			entityManager.createQuery("update Employee set fingerPrintID = " + fingerPrintID + " where employeeNo = :employeeNo")
			.setParameter("employeeNo", employeeNo)
			.executeUpdate();
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public void create(Employee employee, boolean isUser, long roleId)throws Exception{
		openConnection();
		try {
			beginTransaction();
			employee.setEmployeeID(KeyGenerator.generateKey(entityManager));
			employee.setStatus(RecordStatus.active);
			employee.setFingerPrintID(FingerPrintIDMgr.getFingerPrintID(entityManager));
			entityManager.persist(employee);
			
			EmployeeGroup l_empGroup = entityManager.find(EmployeeGroup.class, employee.getEmployeeGroupID());
			if(l_empGroup.getLeaveSchemeID() != null){
				List<LeaveSchemeDetail> l_details = entityManager.createNamedQuery("LeaveSchemeDetail.findAllByLeaveScheme", LeaveSchemeDetail.class)
						.setParameter("leaveSchemeID", l_empGroup.getLeaveSchemeID())
						.getResultList();
				for (LeaveSchemeDetail l_detail : l_details) {
					LeaveBalance l_balance = new LeaveBalance();
					l_balance.setEmployeeID(employee.getEmployeeID());
					l_balance.setLeaveTypeID(l_detail.getLeaveTypeID());
					Calendar l_cal = Calendar.getInstance();
					l_cal.setTime(new Date());
					l_balance.setYear(l_cal.get(Calendar.YEAR));
					l_balance.setMaximum(l_detail.getDaysPerYear());
					l_balance.setTotal(0);
					entityManager.persist(l_balance);
				}
			}
			
			try {
			if(isUser){
				User l_user = new User();
				l_user.setCreatedBy(employee.getCreatedBy());
				l_user.setCreatedDate(employee.getCreatedDate());
				l_user.setLoginId(employee.getEmployeeNo());
				l_user.setModule("");
				l_user.setPassword("123");
				l_user.setUpdatedBy(employee.getCreatedBy());
				l_user.setUpdatedDate(employee.getCreatedDate());
				l_user.setUserId(KeyGenerator.generateKey(entityManager));
				l_user.setUserName(employee.getName());
				l_user.setStatus(employee.getStatus());
				l_user.setEmail(employee.getEmail());
				
				entityManager.persist(l_user);
				
				UserInBranches l_UserInOffice = new UserInBranches();
				l_UserInOffice.setBranchId(employee.getBranchId());
				l_UserInOffice.setUserId(l_user.getUserId());
				entityManager.persist(l_UserInOffice);
				
				UserInRoles l_userInRole = new UserInRoles();
				l_userInRole.setRoleId(roleId);
				l_userInRole.setUserId(l_user.getUserId());
				entityManager.persist(l_userInRole);
			}
			} catch(Exception ex) {
				throw new UserAlreadyExistException(ex.getMessage());
			}
			
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			if(!(e instanceof UserAlreadyExistException)){
				if(DataDuplicationException.isUniqueKyeError(e))
					throw new DataDuplicationException(e.getMessage());
				else
					throw e;	
			}else
				throw e;
			
		} finally {
			closeConnection();
		}
	}
	
	public void savePersonalInfo(EmployeePersonal empPersonal, String employeeNo)throws Exception{
		openConnection();
		try {
			beginTransaction();
			if(empPersonal.getEmployeePersonalID() == 0){
				Employee l_emp = entityManager.createNamedQuery("Employee.findByEmployeeNo", Employee.class).setParameter("employeeNo", employeeNo).getSingleResult();
				empPersonal.setEmployeeID(l_emp.getEmployeeID());
				empPersonal.setEmployeePersonalID(KeyGenerator.generateKey(entityManager));
				entityManager.persist(empPersonal);
			} else {
				entityManager.merge(empPersonal);
			}
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public void saveEducations(List<EmployeeEducation> educations, String employeeNo)throws Exception{
		openConnection();
		try {
			beginTransaction();
			Employee l_emp = entityManager.createNamedQuery("Employee.findByEmployeeNo", Employee.class).setParameter("employeeNo", employeeNo).getSingleResult();
			entityManager.createNamedQuery("EmployeeEducation.deleteByEmployeeID")
				.setParameter("employeeID", l_emp.getEmployeeID())
				.executeUpdate();
			for (EmployeeEducation l_education : educations) {
				l_education.setEmployeeID(l_emp.getEmployeeID());
				entityManager.persist(l_education);
			}
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public void saveExperiences(List<EmployeeExperience> experiences, String employeeNo)throws Exception{
		openConnection();
		try {
			beginTransaction();
			Employee l_emp = entityManager.createNamedQuery("Employee.findByEmployeeNo", Employee.class).setParameter("employeeNo", employeeNo).getSingleResult();
			entityManager.createNamedQuery("EmployeeExperience.deleteByEmployeeID")
				.setParameter("employeeID", l_emp.getEmployeeID())
				.executeUpdate();
			for (EmployeeExperience l_experience : experiences) {
				l_experience.setEmployeeId(l_emp.getEmployeeID());
				entityManager.persist(l_experience);
			}
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public void update(Employee employee, boolean isUser, long roleId)throws Exception{
		openConnection();
		try {
			beginTransaction();
			Employee l_existingEmp = entityManager.find(Employee.class, employee.getEmployeeID());
			EmployeeGroup l_existingEmpGroup = entityManager.find(EmployeeGroup.class, l_existingEmp.getEmployeeGroupID());
			if(employee.getEmployeeGroupID() != l_existingEmp.getEmployeeGroupID()){
				Calendar l_cal = Calendar.getInstance();
				l_cal.setTime(new Date());
				
				EmployeeGroup l_newEmpGrop = entityManager.find(EmployeeGroup.class, employee.getEmployeeGroupID());
				
				List<LeaveSchemeDetail> l_details = entityManager.createNamedQuery("LeaveSchemeDetail.findAllByLeaveScheme",LeaveSchemeDetail.class)
						.setParameter("leaveSchemeID", l_newEmpGrop.getLeaveSchemeID())
						.getResultList();
				List<LeaveSchemeDetail> l_formerDetails = entityManager.createNamedQuery("LeaveSchemeDetail.findAllByLeaveScheme",LeaveSchemeDetail.class)
						.setParameter("leaveSchemeID", l_existingEmpGroup.getLeaveSchemeID())
						.getResultList();
				
				boolean l_existed = false;
				int l_diff = 0;
				for (LeaveSchemeDetail l_detail : l_details) {
					l_existed = false;
					l_diff = 0;
					for (LeaveSchemeDetail l_formerDetail : l_formerDetails) {
						if(l_detail.getLeaveTypeID() == l_formerDetail.getLeaveTypeID()){
							l_diff = l_detail.getDaysPerYear() - l_formerDetail.getDaysPerYear();
							l_diff = l_diff < 0 ? 0 : l_diff;
							l_existed = true;
							break;
						}
					}
					
					LeaveBalance l_leaveBalance = null; 
					try {
						l_leaveBalance = entityManager.createNamedQuery("LeaveBalance.findByEmployeeIdLeaveTypeYear", LeaveBalance.class)
								.setParameter("employeeID", employee.getEmployeeID())
								.setParameter("leaveTypeID", l_detail.getLeaveTypeID())
								.setParameter("year", l_cal.get(Calendar.YEAR))
								.getSingleResult();
					} catch (Exception e) {
						
					}
					if(l_leaveBalance == null){
						l_leaveBalance = new LeaveBalance();
						l_leaveBalance.setMaximum(l_detail.getDaysPerYear());
						l_leaveBalance.setEmployeeID(employee.getEmployeeID());
						l_leaveBalance.setLeaveTypeID(l_detail.getLeaveTypeID());
						l_leaveBalance.setYear(l_cal.get(Calendar.YEAR));
						l_leaveBalance.setTotal(0);
						entityManager.persist(l_leaveBalance);
					}else{
						if(!l_existed){
							l_diff = l_detail.getDaysPerYear() - l_leaveBalance.getTotal();
							l_diff = l_diff < 0 ? 0 : l_diff;
						}
						l_leaveBalance.setTotal(l_leaveBalance.getTotal() + l_diff);
						entityManager.merge(l_leaveBalance);
					}
				}
			}
			if(isUser){
				
				User l_user = null;
				try {
					l_user = entityManager.createNamedQuery("User.findByLoginId", User.class)
							.setParameter("loginId", employee.getEmployeeNo())
							.getSingleResult();
				} catch (NoResultException innerEx) {
					
				}
				
				if(l_user == null){
					l_user = new User();
					l_user.setCreatedBy(employee.getCreatedBy());
					l_user.setCreatedDate(employee.getCreatedDate());
					l_user.setLoginId(employee.getEmployeeNo());
					l_user.setModule("");
					l_user.setPassword("123");
					l_user.setUpdatedBy(employee.getCreatedBy());
					l_user.setUpdatedDate(employee.getCreatedDate());
					l_user.setUserId(KeyGenerator.generateKey(entityManager));
					l_user.setUserName(employee.getName());
					l_user.setStatus(employee.getStatus());
					l_user.setEmail(employee.getEmail());
					entityManager.persist(l_user);
				} else {
					l_user.setUserName(employee.getName());
					l_user.setEmail(employee.getEmail());
					entityManager.merge(l_user);
					
					entityManager.createNamedQuery("UserInBranches.deleteByUserId")
						.setParameter("userId", l_user.getUserId())
						.executeUpdate();
					entityManager.createNamedQuery("UserInRoles.deleteByUserId")
						.setParameter("userId", l_user.getUserId())
						.executeUpdate();
				}
				
				UserInBranches l_UserInOffice = new UserInBranches();
				l_UserInOffice.setBranchId(employee.getBranchId());
				l_UserInOffice.setUserId(l_user.getUserId());
				entityManager.persist(l_UserInOffice);
				
				UserInRoles l_userInRole = new UserInRoles();
				l_userInRole.setRoleId(roleId);
				l_userInRole.setUserId(l_user.getUserId());
				entityManager.persist(l_userInRole);
			}
			entityManager.merge(employee);
			
			entityManager.createNamedQuery("EmployeeEducation.deleteByEmployeeID")
				.setParameter("employeeID", employee.getEmployeeID())
				.executeUpdate();
			entityManager.createNamedQuery("EmployeeExperience.deleteByEmployeeID")
				.setParameter("employeeID", employee.getEmployeeID())
				.executeUpdate();
			
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public void delete(long id)throws Exception{
		openConnection();
		try {
			beginTransaction();
			Employee l_emp = entityManager.find(Employee.class, id);
			l_emp.setStatus(RecordStatus.delete);
			entityManager.merge(l_emp);
			
			User l_user = null;
			try {
				l_user = entityManager.createNamedQuery("User.findByLoginId", User.class)
						.setParameter("loginId", l_emp.getEmployeeNo())
						.getSingleResult();
			} catch (NoResultException innerEx) {
				
			}
			if(l_user != null){
				l_user.setStatus(RecordStatus.delete);
				entityManager.merge(l_user);
			}
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public Employee find(long id)throws Exception{
		openConnection();
		try {
			return entityManager.find(Employee.class, id);
		} catch (Exception e) {
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public List<EmployeeEducation> findEmployeeEducations(long id)throws Exception{
		openConnection();
		try {
			return entityManager.createNamedQuery("EmployeeEducation.findByEmployeeID", EmployeeEducation.class)
					.setParameter("employeeID", id)
					.getResultList();
		} catch (Exception e) {
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public List<EmployeeExperience> findEmployeeExperiences(long id)throws Exception{
		openConnection();
		try {
			return entityManager.createNamedQuery("EmployeeExperience.findByEmployeeID", EmployeeExperience.class)
					.setParameter("employeeID", id)
					.getResultList();
		} catch (Exception e) {
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public EmployeePersonal findEmployeePersonal(long employeeID)throws Exception{
		openConnection();
		try {
			return entityManager.createNamedQuery("EmployeePersonal.findByEmployeeID", EmployeePersonal.class)
					.setParameter("employeeID", employeeID)
					.getSingleResult();
		} catch (NoResultException e) {
			return null;
		} catch (Exception e) {
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public Employee findByEMPNo(String empNo)throws Exception{
		openConnection();
		try {
			try {
				return entityManager.createNamedQuery("Employee.findByEmployeeNo", Employee.class)
						.setParameter("employeeNo", empNo)
						.getSingleResult();
			} catch (NoResultException ex) {
				return null;
			}
		} catch (Exception e) {
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public Employee findByName(String name)throws Exception{
		openConnection();
		try {
			try {
				return entityManager.createNamedQuery("Employee.findByName", Employee.class)
						.setParameter("name", name)
						.getSingleResult();
			} catch (NoResultException innerEx) {
				return null;
			}
			
		} catch (Exception e) {
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public List<Employee> getAll(long branchId)throws Exception{
		openConnection();
		try {
			return entityManager.createNamedQuery("Employee.findAllByBranch", Employee.class)
					.setParameter("branchId", branchId)
					.setParameter("status", RecordStatus.delete)
					.getResultList();
		} catch (Exception e) {
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	@SuppressWarnings("unchecked")
	public EmployeeDataList findEmployees(CriteriaParams cri)throws Exception{
		openConnection();
		try {
            String l_cri = " AND (e.employeeNo LIKE '%" + cri.getKeyword() + "%'"
            		+ " OR e.name LIKE '%" + cri.getKeyword() + "%')";
            
            String l_query = "SELECT e, jt.title FROM Employee as e, JobTitle as jt"
            		+ " WHERE e.jobTitleID = jt.jobTitleID" + l_cri;
            List<Object[]> l_results = entityManager.createQuery(l_query)
                    .setFirstResult(cri.getIndex())
                    .setMaxResults(cri.getSize())
                    .getResultList();
            
            List<Employee>  l_entityList = new ArrayList<Employee>();
            for (Object[] l_objArr : l_results) {
				Employee l_emp = (Employee)l_objArr[0];
				l_emp.setJobTitleName(l_objArr[1].toString());
				l_entityList.add(l_emp);
			}
            
            EmployeeDataList l_dataList = new EmployeeDataList();
            l_dataList.setEntityList(l_entityList);
            
            if(cri.getIndex() == 0){
            	l_query = "SELECT count(e.employeeID) FROM Employee as e, JobTitle as jt"
                		+ " WHERE e.jobTitleID = jt.jobTitleID" + l_cri;
            	l_dataList.setTotalRecord(Long.parseLong(entityManager.createQuery(l_query).getSingleResult().toString()));
            }
            
			return l_dataList;
		} catch (Exception e) {
			throw e;
		} finally{
			closeConnection();
		}
	}
}
